package nl.ceasaro.poker.texasHoldEm.game;

import nl.ceasaro.playingcards.Card;
import nl.ceasaro.playingcards.CardContainer;
import nl.ceasaro.poker.ranking.PokerHand;
import nl.ceasaro.poker.ranking.PokerRanker;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Created by IntelliJ IDEA.
 * User: cees
 * Date: Apr 12, 2007
 * Time: 10:48:02 PM
 * <p/>
 * Simulates a Texax hold'em poker table.
 * It has one CardContainer with 52 cards and a number of players.
 * It handles dealing the playingcard for the players, the flop, the turn and the river.
 */
public class TexasGameManager
{
   public static final String POKER_GAME = "Texas hold'em";
   private static final Log log = LogFactory.getLog(TexasGameManager.class);

   enum Round
   {
      POCKET_CARDS, FIRST_BETTING, SECOND_BETTING, THIRD_BETTING, LAST_BETTING, FINISHED
   }

   //local fields

   // properties
   private CardContainer pokerCards;
   private TexasTable texasTable;
   private PokerRanker pokerRanker;

   public TexasGameManager()
   {
      texasTable = new TexasTable();
   }

   public TexasGameManager(String... playerNames)
   {
      texasTable = new TexasTable();
      TexasPlayer player;
      for (String playerName : playerNames)
      {
         player = new TexasPlayer(playerName);
         getTexasTable().add(player);
      }
   }

   public void takeSeat(TexasPlayer texasPlayer)
   {
      getTexasTable().add(texasPlayer);
   }

   public void playRound()
   {
      switch (getCurrentRound())
      {
         case POCKET_CARDS:
            dealPocketCards();
            break;
         case FIRST_BETTING:
            betting();
            break;
         case SECOND_BETTING:
            betting();
            break;
         case THIRD_BETTING:
            betting();
            break;
         case LAST_BETTING:
            betting();
            break;
         default:
            dealPocketCards();
      }
   }


   /**
    *
    */
   protected void showDown()
   {
      if (getCurrentRound() == Round.LAST_BETTING)
      {
         log.debug("show down.");
         List<Card> communityCards = getTexasTable().getCommunityCards().getCards();
         Set<TexasPlayer> players = getTexasTable().getPlayers();
         for (TexasPlayer player : players)
         {
            ArrayList<Card> playerCards = new ArrayList<Card>();
            playerCards.addAll(communityCards);
            playerCards.addAll(player.getCards());
            PokerHand pokerHand = getPokerRanker().getHighestRank(playerCards);
            player.setPokerHand(pokerHand);
            log.debug(MessageFormat.format("player {0} has a {1}", player.getName(), pokerHand.getRank()));
         }
         setCurrentRound(Round.FINISHED);
      }
   }

   /**
    * @todo pass the dealer token
    * Deals every player at the table 2 cards.
    */
   protected void dealPocketCards()
   {
      if (getCurrentRound() == Round.POCKET_CARDS)
      {
         reset();
         Set<TexasPlayer> players = getTexasTable().getPlayers();
         for (TexasPlayer texasPlayer : players)
         {
            texasPlayer.dealCards(getPokerCards().getCard(), getPokerCards().getCard());
         }
         setCurrentRound(Round.FIRST_BETTING);
      }
   }

   /**
    * turn the flop cards on the table
    */
   protected void turnTheFlop()
   {
      if (getCurrentRound() == Round.FIRST_BETTING)
      {
         CommunityCards communityCards = getTexasTable().getCommunityCards();
         communityCards.setFlop1(getPokerCards().getCard());
         communityCards.setFlop2(getPokerCards().getCard());
         communityCards.setFlop3(getPokerCards().getCard());
         setCurrentRound(Round.SECOND_BETTING);
      }
   }

   /**
    * turn the turn card on the table
    */
   protected void turnTheTurn()
   {
      if (getCurrentRound() == Round.SECOND_BETTING)
      {
         CommunityCards communityCards = getTexasTable().getCommunityCards();
         communityCards.setTurn(getPokerCards().getCard());
         setCurrentRound(Round.THIRD_BETTING);
      }
   }

   /**
    * turn the river card on the table
    */
   protected void turnTheRiver()
   {
      if (getCurrentRound() == Round.THIRD_BETTING)
      {
         CommunityCards communityCards = getTexasTable().getCommunityCards();
         communityCards.setRiver(getPokerCards().getCard());
         setCurrentRound(Round.LAST_BETTING);
      }
   }

   /**
    * player bet there money in the poker pot
    *
    * @todo implement the actual betting
    */
   protected void betting()
   {
      if (getCurrentRound() == Round.FIRST_BETTING)
      {
         if (allPlayersBet())
         {
            turnTheFlop();
         }
      }
      else if (getCurrentRound() == Round.SECOND_BETTING)
      {
         if (allPlayersBet())
         {
            turnTheTurn();
         }
      }
      else if (getCurrentRound() == Round.THIRD_BETTING)
      {
         if (allPlayersBet())
         {
            turnTheRiver();
         }
      }
      else if (getCurrentRound() == Round.LAST_BETTING)
      {
         if (allPlayersBet())
         {
            showDown();
         }
      }
   }

   /**
    * @return true if all players finished there betting
    */
   private boolean allPlayersBet()
   {
      return true;
   }

   /**
    * reset the table this includes shuffling a new deck of playingcards
    * and initializing the pot.
    */
   public void reset()
   {
      pokerCards.reset();
      pokerCards.shuffle();
      texasTable.reset();
      setCurrentRound(Round.POCKET_CARDS);
   }

   /**
    * @return the current round of the table.
    */
   public Round getCurrentRound()
   {
      return getTexasTable().getCurrentRound();
   }

   /**
    * set the currentRound of the texasTable only created for easy of use for this manager
    *
    * @param currentRound the current poker round see: {@link Round}.
    */
   private void setCurrentRound(Round currentRound)
   {
      getTexasTable().setCurrentRound(currentRound);
   }

   public CardContainer getPokerCards()
   {
      return pokerCards;
   }

   public void setPokerCards(CardContainer pokerCards)
   {
      this.pokerCards = pokerCards;
   }

   public TexasTable getTexasTable()
   {
      return texasTable;
   }

   private PokerRanker getPokerRanker()
   {
      return pokerRanker;
   }

   public void setPokerRanker(PokerRanker pokerRanker)
   {
      this.pokerRanker = pokerRanker;
   }
}
